
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavdevice/avdevice.h>

int idx = 0, limit = 0, sum = 1;
uint8_t *data0 = NULL, *data = NULL, value;
void save_image(AVFrame *frame, int idx)
{
  enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_RGB24;
  AVFrame *dst_frame = NULL;
  struct SwsContext *sws_ctx;
  int ret;

  printf("[DEBUG] L%d frame->width:%d height:%d format:%s\n",
      __LINE__,
      frame->width, frame->height, av_get_pix_fmt_name(frame->format));
  dst_frame = av_frame_alloc();
  dst_frame->format = dst_pix_fmt;
  dst_frame->width = frame->width;
  dst_frame->height = frame->height;
  printf("[DEBUG] dst_frame->width:%d height:%d format:%s\n",
      dst_frame->width, dst_frame->height,
      av_get_pix_fmt_name(dst_frame->format));

  sws_ctx = sws_getContext(frame->width, frame->height, frame->format,
      dst_frame->width, dst_frame->height, dst_frame->format,
      SWS_BILINEAR, NULL, NULL, NULL);
  if (!sws_ctx) {
    printf("[ERROR]\n");
    exit(1);
  }

  if ((ret = av_image_alloc(dst_frame->data, dst_frame->linesize,
                            dst_frame->width, dst_frame->height, dst_frame->format, 1)) < 0) {
      fprintf(stderr, "Could not allocate destination image\n");
      return ;
  }
  int dst_bufsize;
  dst_bufsize = ret;
  sws_scale(sws_ctx, (const uint8_t * const*)frame->data, frame->linesize, 0,
      dst_frame->height, dst_frame->data, dst_frame->linesize);

  char filename[20];
  FILE *file = NULL;

  sprintf(filename, "obj/frame%d.ppm", idx);
  file = fopen(filename, "wb");
  fprintf(file, "P6\n%d %d\n255\n", frame->width, frame->height);
  int i;
  uint8_t rgb[3], gray;
  if (idx == 0) {
    data0 = dst_frame->data[0];
    fwrite(dst_frame->data[0], 1, dst_bufsize, file);
  } else {
    data = dst_frame->data[0];
    for (i = 0; i < dst_bufsize/3; i++) {
      rgb[0] = data[3*i];
      rgb[1] = data[3*i+1];
      rgb[2] = data[3*i+2];
      gray = (rgb[0]*19595 + rgb[0]*38469 + rgb[0]*7472) >> 16;
      fwrite(&gray, 1, 1, file);
      fwrite(&gray, 1, 1, file);
      fwrite(&gray, 1, 1, file);
    }
  }
  fclose(file);
}
int read_frame (AVFormatContext *ifmt_ctx, int videoStreamIndex,
    AVCodecContext *dec_ctx)
{
  int ret = 0, stream_index = 0;
  AVPacket *packet = NULL;
  AVFrame *frame = av_frame_alloc();
  AVFrame *dec_frame = NULL;

  packet = av_packet_alloc();
  while (1) {
    if ((ret = av_read_frame(ifmt_ctx, packet)) < 0)
      break;
    stream_index = packet->stream_index;
    av_log (NULL, AV_LOG_DEBUG, "gave frame of stream #%u\n", stream_index);
    if (stream_index == videoStreamIndex) {
      ret = avcodec_send_packet(dec_ctx, packet);
      if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");
        break;
      }
      while (ret >= 0) {
        if (!(dec_frame = av_frame_alloc()))
          return AVERROR(ENOMEM);
        ret = avcodec_receive_frame(dec_ctx, dec_frame);
        if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
          break;
        else if (ret < 0)
          goto end;
        if (ret == 0) {
          save_image(dec_frame, idx);
          idx++;
          //if (idx > 4)
          if (idx > sum)
            exit(1);
          printf("----------\n");
        }
        //exit(1);
      }
      av_packet_unref(packet);
    }
  }

  av_frame_free(&frame);
  av_frame_free(&dec_frame);
end:
  return 0;
}


static int open_input_file(const char *filename)
{
    int ret;
    static AVFormatContext *ifmt_ctx;
    unsigned int i;

    ifmt_ctx = NULL;
    if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open input file:%s\n", filename);
        return ret;
    }

    if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
        return ret;
    }

    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        AVStream *stream = ifmt_ctx->streams[i];
        const AVCodec *dec = avcodec_find_decoder(stream->codecpar->codec_id);
        AVCodecContext *codec_ctx;
        if (!dec) {
            av_log(NULL, AV_LOG_ERROR, "Failed to find decoder for stream #%u\n", i);
            return AVERROR_DECODER_NOT_FOUND;
        }
        codec_ctx = avcodec_alloc_context3(dec);
        if (!codec_ctx) {
            av_log(NULL, AV_LOG_ERROR, "Failed to allocate the decoder context for stream #%u\n", i);
            return AVERROR(ENOMEM);
        }
        ret = avcodec_parameters_to_context(codec_ctx, stream->codecpar);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context "
                   "for stream #%u\n", i);
            return ret;
        }

        /* Inform the decoder about the timebase for the packet timestamps.
         * This is highly recommended, but not mandatory. */
        codec_ctx->pkt_timebase = stream->time_base;

        /* Reencode video & audio and remux subtitles etc. */
        if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
                || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
            if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
                codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, stream, NULL);
            /* Open decoder */
            ret = avcodec_open2(codec_ctx, dec, NULL);
            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);
                return ret;
            }
            printf("[DEBUG] codec_ctx-> width:%d height:%d pix_fmt:%s\n",\
                codec_ctx->width, codec_ctx->height,
                av_get_pix_fmt_name(codec_ctx->pix_fmt));
            read_frame(ifmt_ctx, i, codec_ctx);
        }
    }

    av_dump_format(ifmt_ctx, 0, filename, 0);
    avformat_close_input(&ifmt_ctx);
    return 0;
}

int main(int argc, char **argv)
{
    int ret;
/*
    if (argc != 2) {
        av_log(NULL, AV_LOG_ERROR, "Usage: %s <input file>\n", argv[0]);
        av_log(NULL, AV_LOG_ERROR, "examples: %s /dev/video0\n", argv[0]);
        return 1;
    }
    */
    avdevice_register_all();
    if ((ret = open_input_file(argv[1])) < 0)
        goto end;
    limit = atoi(argv[2]);
end:
    if (ret < 0)
        av_log(NULL, AV_LOG_ERROR, "Error occurred: %s\n", av_err2str(ret));
    return ret ? 1 : 0;
}
